<template>
  <div class="picContainer">
    <div class="title">
      鼠标 x:{{ mousePosition.x.toFixed(1) }} y:{{ mousePosition.y.toFixed(1) }} z:{{ mousePosition.z.toFixed(1) }}
    </div>
    <div ref="threeBoxRef" class="box">

    </div>
    <template v-for="(o,index) in cubes" :key="index" >
      <div class="info1"  :style="o.dynamicStyle"> {{o.name}} </div>
    </template>
  </div>
</template>

<script lang="ts">
import {defineComponent, onMounted, reactive, ref, toRefs} from 'vue';
import * as THREE from 'three';
// 引入扩展库OrbitControls.js
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
// 引入扩展库GLTFLoader.js
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {DoubleSide} from "three/src/constants";
import {Color} from "three/src/math/Color";
import {FontLoader} from 'three/addons/loaders/FontLoader.js'
import {TextGeometry} from 'three/addons/geometries/TextGeometry.js';
import sensorXYZ from "/@/views/iotApply/iotLocate/locate/component2/sensorXYZ.vue";
import {ElMessage} from "element-plus";
import {BufferGeometry} from "three";
import {
  computeScaleRate,
  defaultThreeData,
  saveFloatFixByScaleRate
} from "/@/views/iotApply/iotLocate/locate/component2/commonThree";

import threefont from '/@/assets/qc/threefont.json';

export default defineComponent({
  name: 'cubeLocate3d',
  components:{
    sensorXYZ
  },
  setup(props,{emit}) {
    const threeBoxRef = ref()
    const info1Ref = ref()
    var mouse = new THREE.Vector2();
    let scene = new THREE.Scene();
    // 创建渲染器对象
    const renderer = new THREE.WebGLRenderer();
    // 实例化一个透视投影相机对象
    const camera = new THREE.PerspectiveCamera();
    const raycaster = new THREE.Raycaster();

    let customGridMesh: THREE.Mesh| null = null

    let axisLineMeshes = []
    let axisTextMeshes = []

    const state = reactive({
      mousePosition: {
        x: 0, y: 0, z: 0
      },
      threeData:{
        width: 600,
        height: 600,
        compareSize:200,  //这个值用来进行对比比例
        bgColor:"#C0C0C0",
        scaleRate: 1,    // 记录form表单的数据，包括所有的坐标，应该以什么比例进行缩放。当数据接近或者大于width时，会明显变形，这个值就是为了避免这种情况。
      },
      pFormData:{
        xrange:1000,
        yrange:1000,
        zrange:1000,
        sensorCount:8,
        viewType:0,
        showColumns:["no","x","y","z"],
        tableData:[], //传感器用户坐标
        measureDistanceType:0,
      },
      formData:{
        xrange:1000,
        yrange:1000,
        zrange:1000,
        sensorCount:8,
        viewType:0,
        showColumns:["no","x","y","z"],
        tableData:[], //传感器用户坐标
        measureDistanceType:0,
      },
      cubes: [],
    });

    // 页面加载时
    onMounted(() => {
      initThreeData()
      renderPicOnce()
    });

    const initThreeData = ()=>{
      state.threeData = defaultThreeData()
    }

    function RandomNumBoth(Min, Max) {
      var Range = Max - Min;
      var Rand = Math.random();
      var num = Min + Math.round(Rand * Range); //四舍五入
      return num;
    }

    const renderPicOnce = ()=> {
      window.addEventListener("mousemove", listenMouseMove)
      renderer.setSize(state.threeData.width, state.threeData.height); //设置three.js渲染区域的尺寸(像素px)
      document.body.appendChild(renderer.domElement);

      // 添加光源
      const light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(0,state.threeData.width, state.threeData.height);
      scene.add(light);

      threeBoxRef.value.appendChild(renderer.domElement);

      //相机观察目标指向Threejs 3D空间中某个位置
      camera.lookAt(scene.position); //坐标原点
      // 设置相机的视口与渲染器的尺寸相同
      camera.aspect = renderer.domElement.width / renderer.domElement.height;
      camera.updateProjectionMatrix();

      camera.position.set(0, -600, 0);

      // 添加轨道控制器
      const controls = new OrbitControls(camera, renderer.domElement)
      controls.addEventListener('change', function () {
        updateAllTextPosition()
        renderer.render(scene, camera); //执行渲染操作
      })

      renderPic()
    }

    // 清除画布
    function clearCanvas() {
      renderer.clear();
    }

    const renderPic = ()=> {
      // 清除画布
      clearCanvas();
      scene.background = new THREE.Color(state.threeData.bgColor)
      createAxis();
      createCustomGrid()
      createSensorBoxes()

      setTimeout(() => {
        renderer.render(scene, camera); //执行渲染操作
      }, 10)
    }

    const createCustomGrid = () => {
      if(customGridMesh){
        customGridMesh.removeFromParent()
      }
      const cubeGeometry = new THREE.BoxGeometry(state.formData.xrange, state.formData.yrange, state.formData.zrange,1,1,1); // 正方体的尺寸
      let material = new THREE.MeshBasicMaterial({
        color: 0xdadada,
        wireframe: true, // 使用线框模式以显示段落轮廓
        transparent: true,
        opacity: 0.8,  // 1完全不透明
      });
      const mesh = new THREE.Mesh(cubeGeometry, material);
      customGridMesh = mesh
      mesh.position.set(0,0,0)
      scene.add(mesh);
    }

    const createSensorBoxes = ()=>{
      if(!state.formData || !state.formData.tableData){
        return
      }
      state.cubes.map((item,index)=>{
        if(item.cube){
          item.cube.removeFromParent()
        }
      })
      state.cubes = []
      let tmp = []
      state.formData.tableData.map((item,index)=>{
        const cube = createCubeItem(5)
        const cubexyz = transferPlaneDataTo3dData(item.x,item.y,item.z)
        cube.position.set(cubexyz.x, cubexyz.y, cubexyz.z); // 调整位置以适应您的需求
        scene.add(cube);
        tmp.push({
          name: item.no,
          dynamicStyle: {
            left: "0px",
            top: "0px",
          },
          cube:cube
        })
      })
      state.cubes = tmp
    }

    const transferPlaneDataTo3dData = (x:number, y:number,z:number) => {
      return {
        x: x-state.formData.xrange / 2,
        y: y-state.formData.yrange / 2,
        z: z-state.formData.zrange / 2,
      }
    }

    // 创建刻度
    function createAxis() {
      var fontLoader = new FontLoader();
      var axisLabelFont;
      //'https://threejs.org/examples/fonts/helvetiker_regular.typeface.json'
      fontLoader.load("/threefont.json", function (font) {
        console.log(font)
        axisLabelFont = font;
        createAxisHelper(axisLabelFont);
      });
    }


    function createAxisHelper(font:any) {
      if(axisLineMeshes.length > 0){
        axisLineMeshes.map((item:THREE.Line)=>{
          item.removeFromParent()
        })
      }
      if(axisTextMeshes.length > 0){
        axisTextMeshes.map((item:THREE.Mesh)=>{
          item.removeFromParent()
        })
      }
      axisLineMeshes = []
      axisTextMeshes = []

      var materialX = new THREE.LineBasicMaterial({ color: 0xff0000 }); // 红色X轴
      var materialY = new THREE.LineBasicMaterial({ color: 0x00ff00 }); // 绿色Y轴
      var materialZ = new THREE.LineBasicMaterial({ color: 0x0000ff }); // 蓝色Z轴

      const xmin = 0
      const xmax = Math.abs( state.formData.xrange)
      const xstep = ( xmax - xmin) / 10
      const xhalf = xmax / 2

      const ymin = 0
      const ymax = Math.abs( state.formData.yrange)
      const ystep = ( ymax - ymin) / 10
      const yhalf = ymax / 2

      const zmin = 0
      const zmax = Math.abs( state.formData.zrange)
      const zstep = ( zmax - zmin) / 10
      const zhalf = zmax/ 2

      // X轴刻度和刻度值
      let xcount = 0
      for (var i = xmin; i <= xmax; i+=xstep) {
        // var geometry = new THREE.BufferGeometry();
        // var positions = new Float32Array([
        //     i, 0, 0,
        //     i, 0.2, 0
        // ]);
        // geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        // var line = new THREE.Line(geometry, materialX);
        // axisLineMeshes.push(line)
        // scene.add(line);

        if(xcount%2 == 0){
          const textLabel = (i* state.threeData.scaleRate).toFixed(0)
          var textGeometry = new TextGeometry(textLabel, {
            font: font,
            size: parseInt((xstep/4).toString()),
            height: 1
          });
          var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
          var textMesh = new THREE.Mesh(textGeometry, textMaterial);
          textMesh.position.set(i-xhalf, -0.5-yhalf, 0.2-zhalf);
          axisTextMeshes.push(textMesh)
          scene.add(textMesh);
        }

        xcount++
      }

      // Y轴刻度和刻度值
      for (var i = ymin; i <= ymax; i+=ystep) {
        // var geometry = new THREE.BufferGeometry();
        // var positions = new Float32Array([
        //     0, i, 0,
        //     0.2, i, 0
        // ]);
        // geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        // var line = new THREE.Line(geometry, materialY);
        // axisLineMeshes.push(line)
        // scene.add(line);

        const textLabel = (i* state.threeData.scaleRate).toFixed(0)
        var textGeometry = new TextGeometry(textLabel, {
          font: font,
          size: parseInt((ystep/4).toString()),
          height: 1
        });
        var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
        var textMesh = new THREE.Mesh(textGeometry, textMaterial);
        textMesh.position.set(-0.5-xhalf, i-yhalf, 0.2-zhalf);
        axisTextMeshes.push(textMesh)
        scene.add(textMesh);
      }

      // Z轴刻度和刻度值
      for (var i = zmin; i <= zmax;  i+=zstep) {
        // var geometry = new THREE.BufferGeometry();
        // var positions = new Float32Array([
        //     0, 0, i,
        //     0.2, 0, i
        // ]);
        // geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        // var line = new THREE.Line(geometry, materialZ);
        // axisLineMeshes.push(line)
        // scene.add(line);
        const textLabel = (i* state.threeData.scaleRate).toFixed(0)
        var textGeometry = new TextGeometry(textLabel, {
          font: font,
          size: parseInt((zstep/4).toString()),
          height:1
        });
        var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
        var textMesh = new THREE.Mesh(textGeometry, textMaterial);
        textMesh.position.set(-0.5-xhalf, 0.2-yhalf, i-zhalf);
        axisTextMeshes.push(textMesh)
        scene.add(textMesh);
      }

      // XYZ轴线
      var xAxisGeometry = new THREE.BufferGeometry();
      var xAxisPositions = new Float32Array([
        xmin, 0, 0,
        xmax+xstep, 0, 0
      ]);
      xAxisGeometry.setAttribute('position', new THREE.BufferAttribute(xAxisPositions, 3));
      var xAxisLine = new THREE.Line(xAxisGeometry, materialX);
      xAxisLine.position.set(-xhalf, -yhalf, -zhalf);
      axisLineMeshes.push(xAxisLine)
      scene.add(xAxisLine);

      var yAxisGeometry = new THREE.BufferGeometry();
      var yAxisPositions = new Float32Array([
        0, ymin, 0,
        0, ymax+ystep, 0
      ]);
      yAxisGeometry.setAttribute('position', new THREE.BufferAttribute(yAxisPositions, 3));
      var yAxisLine = new THREE.Line(yAxisGeometry, materialY);
      yAxisLine.position.set(-xhalf, -yhalf, -zhalf);
      axisLineMeshes.push(yAxisLine)
      scene.add(yAxisLine);

      var zAxisGeometry = new THREE.BufferGeometry();
      var zAxisPositions = new Float32Array([
        0, 0, zmin,
        0, 0, zmax+zstep
      ]);
      zAxisGeometry.setAttribute('position', new THREE.BufferAttribute(zAxisPositions, 3));
      var zAxisLine = new THREE.Line(zAxisGeometry, materialZ);
      zAxisLine.position.set(-xhalf, -yhalf, -zhalf);
      axisLineMeshes.push(zAxisLine)
      scene.add(zAxisLine);

      renderer.render(scene, camera); //执行渲染操作
    }



    function updateAllTextPosition(){
      state.cubes.map((item,index)=>{
        if(item.cube){
          updateTextPosition(index,item.cube)
        }
      })
    }

    function updateTextPosition(index, cube) {
      const vector = new THREE.Vector3();
      cube.getWorldPosition(vector);
      vector.project(camera);
      const x = (vector.x * .5 + .5) * state.threeData.width;
      const y = (-vector.y * .5 + .5) * state.threeData.height;
      state.cubes[index].dynamicStyle.left = x + "px"
      state.cubes[index].dynamicStyle.top = y + "px"
    }


    function createCubeItem(size:number) {
      const cubeGeometry = new THREE.BoxGeometry(size, size, size); // 正方体的尺寸
      const material = new THREE.MeshBasicMaterial({color: "#0000ee"});
      const cube = new THREE.Mesh(cubeGeometry, material);
      return cube;
    }

    function addPoints() {
      const geometry = new THREE.SphereGeometry(20, 8, 8);
      const material = new THREE.MeshBasicMaterial({color: 0xff0000});
      const sphere = new THREE.Mesh(geometry, material);
      sphere.position.set(RandomNumBoth(-400, 400), RandomNumBoth(-400, 400), RandomNumBoth(-100, 100)); // 调整位置以适应您的需求
      scene.add(sphere);
    }

    const listenMouseMove = (event: any) => {
      const px = event.offsetX;
      const py = event.offsetY;
      //屏幕坐标px、py转标准设备坐标x、y
      //width、height表示canvas画布宽高度
      const x = (px / state.threeData.width) * 2 - 1;
      const y = -(py / state.threeData.height) * 2 + 1;
      mouse.x = x
      mouse.y = y
      // 将鼠标坐标转换为世界坐标
      raycaster.setFromCamera(mouse, camera);
      // 使用光线投射器来获取与射线相交的物体
      const intersects = raycaster.intersectObjects(scene.children, true);
      if (intersects.length > 0) {
        var intersection = intersects[0];
        var position = intersection.point;

        // 在控制台中打印三维坐标
        //console.log('Mouse Position:', position);
        position.x = Number(position.x)*state.threeData.scaleRate + (state.formData.xrange/2)*state.threeData.scaleRate
        position.y = Number(position.y)*state.threeData.scaleRate + (state.formData.yrange/2)*state.threeData.scaleRate
        position.z = Number(position.z)*state.threeData.scaleRate + (state.formData.zrange/2)*state.threeData.scaleRate
        state.mousePosition = position
      }
    }

    const reRender = (pFormData:any)=>{
      updateFormData(pFormData)
      renderPic()
      updateAllTextPosition()

      setTimeout(()=>{
        renderer.render(scene, camera); //执行渲染操作
      },10)

    }

    const updateFormData = (pFormData:any) => {
      state.pFormData = Object.assign({},pFormData)
      changePForm2Form()
    }

    const changePForm2Form = ()=> {
      const maxdata = getPFormMaxData(state.pFormData)
      const scaleRate = computeScaleRate(maxdata,state.threeData.compareSize)
      state.threeData.scaleRate = scaleRate
      const tofixed = saveFloatFixByScaleRate(scaleRate)
      state.formData = Object.assign({},state.pFormData)
      state.formData.xrange = Number((Number(state.pFormData.xrange) / scaleRate).toFixed(tofixed))
      state.formData.yrange = Number((Number(state.pFormData.yrange) / scaleRate).toFixed(tofixed))
      state.formData.zrange = Number((Number(state.pFormData.zrange) / scaleRate).toFixed(tofixed))

      if(state.pFormData.tableData){
        let tabledataTmp = []
        state.pFormData.tableData.map((item:any,index:number)=>{
          const copyItem = Object.assign({},item)
          copyItem.x = Number((Number(item.x) / scaleRate).toFixed(tofixed))
          copyItem.y = Number((Number(item.y) / scaleRate).toFixed(tofixed))
          copyItem.z = Number((Number(item.z) / scaleRate).toFixed(tofixed))
          tabledataTmp.push(copyItem)
        })
        state.formData.tableData = tabledataTmp
      }
    }
    const getPFormMaxData = (pFormData:any)=>{
      let max =pFormData.xrange
      if(pFormData.yrange > max){
        max = pFormData.yrange
      }
      if(pFormData.zrange > max){
        max = pFormData.zrange
      }
      return max
    }


    return {
      threeBoxRef,
      info1Ref,
      ...toRefs(state),
      reRender,
    }
  }
})
</script>

<style scoped lang="scss">
.picContainer{
  display: flex;
  flex-direction: column;
  position: relative;
  .title{
    width: 600px;
    text-align: center;
  }
  .box{

  }
}
.info1 {
  position: absolute;
  top: 0;
  z-index: 100;
  display:inline-block;
  line-height: 1;
  color:orangered;
  font-weight: bold;
  font-size: 13px;
}
body {
  margin: 0;
}

canvas {
  display: block;
}

.label {
  position: absolute;
  color: black;
  font-size: 14px;
}

</style>




